#include <ptgui/ptgui.h>
#include <ptgui/font.h>
#include <ptgui/image.h>
#include <ptgui/event.h>
#include <ptgui/system.h>

void ptgui_system_init()
{
	/* init GUI content */
	ptgui_gc_system_init();
	/* init image */
	ptgui_image_system_init();
	/* init font */
	ptgui_font_system_init();
}

void ptgui_assert(const rt_int8_t *str, rt_int32_t line)
{
	rt_kprintf("assert: %s:%d", str, line);

	while (1);
}

#define DBG_MSG(x)
#define ptgui_event_dump(tid, event)

struct rt_semaphore _ptgui_thread_hash_semaphore;
void ptgui_thread_system_init()
{
	rt_sem_init(&_ptgui_thread_hash_semaphore, "ptgui", 1, RT_IPC_FLAG_FIFO);
}

ptgui_thread_t * ptgui_thread_register(rt_thread_t tid, rt_mq_t mq)
{
	ptgui_thread_t *thread = ptgui_malloc(sizeof(struct ptgui_thread));

	if (thread != RT_NULL) {
		DBG_MSG(("register a ptgui thread: %s, tid: 0x%p\n", tid->name, tid));

		/* set tid and mq */
		thread->tid = tid;
		thread->mq = mq;
		thread->owner = RT_NULL;

		/* take semaphore */
		rt_sem_take(&_ptgui_thread_hash_semaphore, RT_WAITING_FOREVER);
		/* set user thread */
		tid->user_data = (rt_uint32_t)thread;
		/* release semaphore */
		rt_sem_release(&_ptgui_thread_hash_semaphore);
	}

	return thread;
}

void ptgui_thread_deregister(rt_thread_t tid)
{
	struct ptgui_thread *thread;

	/* find ptgui_thread */
	thread = (struct ptgui_thread *)(tid->user_data);

	if (thread != RT_NULL) {
		/* take semaphore */
		rt_sem_take(&_ptgui_thread_hash_semaphore, RT_WAITING_FOREVER);
		/* remove ptgui_thread */
		tid->user_data = 0;
		/* release semaphore */
		rt_sem_release(&_ptgui_thread_hash_semaphore);

		/* free ptgui_thread */
		ptgui_free(thread);
	}
}

rt_err_t ptgui_thread_send(rt_thread_t tid, ptgui_event_t *event, rt_size_t event_size)
{
	rt_err_t result;
	struct ptgui_thread *thread;

	ptgui_event_dump(tid, event);
	/* if (event->type != ptgui_EVENT_TIMER)
		rt_kprintf("event size: %d\n", event_size); */

	/* find ptgui_thread */
	thread = (struct ptgui_thread *)(tid->user_data);
	if (thread == RT_NULL) {
		return -RT_ERROR;
	}

	result = rt_mq_send(thread->mq, event, event_size);
	if (result != RT_EOK) {
		if (event->type != PTGUI_EVENT_TIMER) {
			rt_kprintf("send event to %s failed\n", thread->tid->name);
		}
	}

	return result;
}

rt_err_t ptgui_thread_send_urgent(rt_thread_t tid, ptgui_event_t *event, rt_size_t event_size)
{
	rt_err_t result;
	struct ptgui_thread *thread;

	ptgui_event_dump(tid, event);
	rt_kprintf("event size: %d\n", event_size);

	/* find ptgui_thread */
	thread = (struct ptgui_thread *)(tid->user_data);
	if (thread == RT_NULL) {
		return -RT_ERROR;
	}

	result = rt_mq_urgent(thread->mq, event, event_size);
	if (result != RT_EOK) {
		rt_kprintf("send ergent event failed\n");
	}

	return result;
}

rt_err_t ptgui_thread_send_sync(rt_thread_t tid, ptgui_event_t *event, rt_size_t event_size)
{
	rt_err_t r;
	struct ptgui_thread *thread;
	rt_int32_t ack_buffer,
			   ack_status;
	struct rt_mailbox ack_mb;

	ptgui_event_dump(tid, event);
	rt_kprintf("event size: %d\n", event_size);

	/* init ack mailbox */
	r = rt_mb_init(&ack_mb, "ack", &ack_buffer, 1, 0);
	if (r != RT_EOK) {
		goto __return;
	}

	/* find ptgui_thread */
	thread = (struct ptgui_thread *)(tid->user_data);
	if (thread == RT_NULL) {
		r = RT_ERROR; goto __return;
	}

	event->ack = &ack_mb;
	r = rt_mq_send(thread->mq, event, event_size);
	if (r != RT_EOK) {
		rt_kprintf("send sync event failed\n");
		goto __return;
	}

	r = rt_mb_recv(&ack_mb, (rt_uint32_t *)&ack_status, RT_WAITING_FOREVER);
	if (r != RT_EOK) {
		goto __return;
	}

	if (ack_status != PTGUI_STATUS_OK) {
		r = -RT_ERROR;
	}
	else {
		r = RT_EOK;
	}

	/* fini ack mailbox */
	rt_mb_detach(&ack_mb);

	__return:
	return r;
}

rt_err_t ptgui_thread_ack(ptgui_event_t *event, rt_int32_t status)
{
	if (event != RT_NULL && event->ack != RT_NULL) {
		rt_mb_send(event->ack, status);
	}

	return RT_EOK;
}

rt_err_t ptgui_thread_recv(ptgui_event_t *event, rt_size_t event_size)
{
	struct ptgui_thread *thread;
	rt_err_t r;

	/* find ptgui_thread */
	thread = (struct ptgui_thread *)(rt_thread_self()->user_data);
	if (thread == RT_NULL) {
		return -RT_ERROR;
	}

	r = rt_mq_recv(thread->mq, event, event_size, RT_WAITING_FOREVER);

	return r;
}

rt_err_t ptgui_thread_recv_filter(rt_uint32_t type, ptgui_event_t *event, rt_size_t event_size)
{
	struct ptgui_thread *thread;

	/* find ptgui_thread */
	thread = (struct ptgui_thread *)(rt_thread_self()->user_data);
	if (thread == RT_NULL) {
		return -RT_ERROR;
	}

	while (rt_mq_recv(thread->mq, event, event_size, RT_WAITING_FOREVER) == RT_EOK) {
		if (event->type == type) {
			return RT_EOK;
		}
		else {
#if 0
			/* let widget to handle event */
			if (thread->owner != RT_NULL &&
				thread->widget->event_handler != RT_NULL)
			{
				thread->widget->event_handler(thread->widget, event);
			}
#endif
		}
	}

	return -RT_ERROR;
}

/************************************************************************/
/* ptgui Timer  														*/
/************************************************************************/
static void ptgui_time_out(void *parameter)
{
	ptgui_timer_t *timer;
	struct ptgui_event_timer event;
	timer = (ptgui_timer_t *)parameter;

	/*
	* Note: event_timer can not use ptgui_EVENT_TIMER_INIT to init, for there is no
	* thread context
	*/
	event.parent.type = PTGUI_EVENT_TIMER;
	event.parent.src = NULL;

	event.timer = timer;

	//ptgui_thread_send(timer->tid, &(event.parent), sizeof(struct ptgui_event_timer));
}

ptgui_timer_t * ptgui_timer_create(rt_int32_t time, rt_base_t flag, ptgui_timeout_func timeout, void *parameter)
{
	ptgui_timer_t *timer;

	timer = (ptgui_timer_t *)ptgui_malloc(sizeof(struct ptgui_timer));
	timer->tid = rt_thread_self();
	timer->timeout = timeout;
	timer->user_data = parameter;

	/* init rt-thread timer */
	rt_timer_init(&(timer->timer), "ptgui", ptgui_time_out, timer, time, (rt_uint8_t)flag);

	return timer;
}

void ptgui_timer_destory(ptgui_timer_t *timer)
{
	RT_ASSERT(timer != RT_NULL);

	/* stop timer firstly */
	ptgui_timer_stop(timer);

	/* detach rt-thread timer */
	rt_timer_detach(&(timer->timer));

	ptgui_free(timer);
}

void ptgui_timer_start(ptgui_timer_t *timer)
{
	RT_ASSERT(timer != RT_NULL);

	/* start rt-thread timer */
	rt_timer_start(&(timer->timer));
}

void ptgui_timer_stop(ptgui_timer_t *timer)
{
	RT_ASSERT(timer != RT_NULL);

	/* stop rt-thread timer */
	rt_timer_stop(&(timer->timer));
}